package com.fms.security;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fms.constant.MessageConstant;
import com.fms.entity.AttendanceTask;
import com.fms.entity.SysDept;
import com.fms.entity.SysUser;
import com.fms.entity.TaskDept;
import com.fms.exception.CheckException;
import com.fms.exception.ServiceException;
import com.fms.mapper.*;
import com.fms.service.AttendanceTaskService;
import com.fms.service.SysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.sql.Time;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * <p>检查工具类</p>
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class CheckUtils {

    private final SysUserService sysUserService;

    private final TaskDeptMapper taskDeptMapper;

    private final BCryptPasswordEncoder passwordEncoder;

    private final SysDeptMapper sysDeptMapper;

    private final AttendanceTaskMapper attendanceTaskMapper;

    private final AttendanceTaskService attendanceTaskService;
    private final AttendanceMapper attendanceMapper;

    @Value("${fms.default.circle}")
    private Integer defaultCircle;

    /**
     * 用户名的校验
     * @param username 用户名
     * @param isCheckUnique 是否要验证唯一
     */
    public void checkUsernameSyntax(String username,Boolean isCheckUnique) throws CheckException{
        List<CheckException.Validator<String>> validators = List.of(
                candidate -> (candidate == null || candidate.isEmpty()) ? "用户名不能为空" : null,
                candidate -> (isCheckUnique && sysUserService.getOne(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername,username)) != null)? "用户名已存在" : null
        );
        CheckException.check("无效的用户名", username, validators);
    }

    /**
     * 单纯的密码格式的校验
     * @param password 密码
     */
    public void checkPasswordSyntax(String password) throws CheckException{
        List<CheckException.Validator<String>> validators=List.of(
                candidate -> (candidate == null || candidate.isEmpty()) ? "密码不能为空" : null,
                candidate -> !candidate.matches("^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$")?  "密码长度不能小于8且必须包含数字和字母" : null
        );
        CheckException.check("无效的密码", password, validators);
    }

    /**
     * 密码校验加匹配
     * @param password 密码
     * @param username 用户名
     */
    public void checkPasswordMatcher(String password,String username) throws CheckException{
        List<CheckException.Validator<String>> validators = List.of(
                candidate -> (!passwordEncoder.matches(password, sysUserService
                                .lambdaQuery()
                                .eq(SysUser::getUsername, username)
                                .list().get(0)
                                .getPassword())? "密码输入错误" : null)
        );
        CheckException.check("无效的密码", password, validators);
    }

    /**
     * 时间校验
     * @param deptIds 角色id
     * @param attendanceTaskId 排除任务id，用于区分更新增添
     * @param timeMap 时间Map
     * @throws CheckException 异常
     */
    public void checkIsUseDeptIdOfAttendanceTask(List<Long> deptIds, Long attendanceTaskId,Map<String,LocalTime> timeMap) throws CheckException{
        LocalTime startTime = timeMap.get(MessageConstant.START_TIME);
        LocalTime inDeadline = timeMap.get(MessageConstant.IN_DEADLINE);
        LocalTime outDeadline = timeMap.get(MessageConstant.OUT_DEADLINE);
        List<CheckException.Validator<List<Long>>> validators = List.of(
                candidate -> (candidate == null || candidate.isEmpty()) ? "角色不能为空" : null,
                candidate -> {
                    if(inDeadline.isBefore(startTime)){
                        if(outDeadline.isBefore(inDeadline)){
                            return "签到截止时间不能早于签到开始时间";
                        }
                    }
                    if(outDeadline.isBefore(inDeadline)){
                        if(inDeadline.isBefore(startTime)){
                            return "签到截止时间不能早于任务开始时间";
                        }
                    }
                    return null;
                },
                candidate -> {
                    List<Long> list;
                    if(attendanceTaskId==null){
                        list=deptIds;
                    }else{
                        List<Long> ds = taskDeptMapper.selectList(new LambdaQueryWrapper<TaskDept>()
                                        .eq(TaskDept::getTaskId, attendanceTaskId))
                                .stream().map(TaskDept::getDeptId).toList();
                        list= deptIds.stream().filter(deptId -> !ds.contains(deptId)).toList();
                    }
                    List<String> errorDeptNames=new ArrayList<>();
                    list.forEach(deptId -> {
                        List<Long> taskIds = taskDeptMapper.selectList(new LambdaQueryWrapper<TaskDept>().eq(TaskDept::getDeptId, deptId))
                                .stream().map(TaskDept::getTaskId).toList();
                        if(!taskIds.isEmpty()){
                            List<Map<String, Object>> map = attendanceTaskMapper.selectMaps(new QueryWrapper<AttendanceTask>()
                                    .select("max(out_deadline) as maxOutDeadline,min(start_time) as minStartTime")
                                    .in("id", taskIds));
                            LocalTime maxOutDeadline =((Time) map.get(0).get("maxOutDeadline")).toLocalTime();
                            LocalTime minStartTime = ((Time) map.get(0).get("minStartTime")).toLocalTime();
                            log.info("最晚签退时间{}.最早签到时间：{}",maxOutDeadline,minStartTime);
                            if(startTime.isBefore(maxOutDeadline) || (outDeadline.isAfter(minStartTime)&&outDeadline.isBefore(maxOutDeadline))){
                                errorDeptNames.add(sysDeptMapper.selectById(deptId).getName());
                            }
                        }
                    });
                    return errorDeptNames.isEmpty() ? null : String.join(",",errorDeptNames)+"在此时间段已经定义了打卡任务，不能重复定义";
                }
        );
        CheckException.check("存在无效的角色", deptIds, validators);
    }


    /**
     * 任务操作状态校验
     * @param taskId 任务id
     * @throws CheckException 异常
     */
    public void checkTaskIdOfAttendanceUpdate(Long taskId) throws CheckException{
        List<CheckException.Validator<Long>> validators = List.of(
                candidate -> (candidate == null || candidate < 0) ? "id非法" : null,
                candidate -> {
                    AttendanceTask attendanceTask = attendanceTaskMapper.selectById(candidate);
                    if(attendanceTask==null){
                        return  "id不存在";
                    }
                    Integer status = attendanceTask.getStatus();
                    if(status==2){
                        return "该任务正在进行不可进行操作";
                    }
                    return  null;
            }
        );
        CheckException.check("无效的任务操作请求", taskId, validators);
    }

    /**
     * 任务结束操作状态校验
     * @param taskId 任务id
     * @throws CheckException 异常
     */
    public void checkTaskIdOfAttendanceEnd(Long taskId) throws CheckException{
        List<CheckException.Validator<Long>> validators = List.of(
                candidate -> (candidate == null || candidate < 0) ? "id非法" : null,
                candidate -> {
                    AttendanceTask attendanceTask = attendanceTaskMapper.selectById(candidate);
                    if(attendanceTask==null){
                        return  "id不存在";
                    }
                    Integer status = attendanceTask.getStatus();
                    if(status!=2){
                        return "该任务未在进行无法进行结束操作";
                    }
                    return  null;
            }
        );
        CheckException.check("无效的任务操作请求", taskId, validators);
    }

    /**
     * 打卡距离的验证器
     * @param distance 打卡距离
     * @throws CheckException 异常
     */
    public void checkAttendanceDistance(Double distance) throws CheckException{
        List<CheckException.Validator<Double>> validators=List.of(
                candidate -> (candidate == null || candidate < 0) ? "距离非法" : null,
                candidate -> (candidate >= defaultCircle) ? "距离不在打卡处的"+defaultCircle+"米范围内" : null
        );
        CheckException.check("距离验证失败", distance, validators);
    }


    /**
     * 校验打卡状态
     * @param id id
     * @param status 合法状态
     * @throws CheckException 异常
     */
    public void checkStatusOfAttendance(Long id, List<Integer> status) throws CheckException{
        List<CheckException.Validator<Long>> validators = List.of(
                candidate -> (candidate == null || candidate < 0) ? "任务id不存在" : null,
                candidate -> {
                    Integer realStatus = attendanceMapper.selectById(candidate).getStatus();
                    if(!status.contains(realStatus)){
                        return "打卡操作非法";
                    }
                    return  null;
            }
        );
        CheckException.check("无效的任务操作请求", id, validators);
    }
}
